1   /*                        __    __  __  __    __  ___
2    *                       \  \  /  /    \  \  /  /  __/
3    *                        \  \/  /  /\  \  \/  /  /
4    *                         \____/__/  \__\____/__/.ɪᴏ
5    * ᶜᵒᵖʸʳᶦᵍʰᵗ ᵇʸ ᵛᵃᵛʳ ⁻ ˡᶦᶜᵉⁿˢᵉᵈ ᵘⁿᵈᵉʳ ᵗʰᵉ ᵃᵖᵃᶜʰᵉ ˡᶦᶜᵉⁿˢᵉ ᵛᵉʳˢᶦᵒⁿ ᵗʷᵒ ᵈᵒᵗ ᶻᵉʳᵒ
6    */
7   package io.vavr.collection;
8   
9   /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*\
10     G E N E R A T O R   C R A F T E D
11  \*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
12  
13  import java.io.Serializable;
14  
15  import java.util.Collection;
16  
17  /**
18   * Helper to replace reflective array access.
19   *
20   * @author Pap Lőrinc
21   */
22  interface ArrayType<T> {
23      @SuppressWarnings("unchecked")
24      static <T> ArrayType<T> obj() { return (ArrayType<T>) ObjectArrayType.INSTANCE; }
25  
26      Class<T> type();
27      int lengthOf(Object array);
28      T getAt(Object array, int index);
29  
30      Object empty();
31      void setAt(Object array, int index, T value) throws ClassCastException;
32      Object copy(Object array, int arraySize, int sourceFrom, int destinationFrom, int size);
33  
34      @SuppressWarnings("unchecked")
35      static <T> ArrayType<T> of(Object array)  { return of((Class<T>) array.getClass().getComponentType()); }
36      static <T> ArrayType<T> of(Class<T> type) { return !type.isPrimitive() ? obj() : ofPrimitive(type); }
37      @SuppressWarnings("unchecked")
38      static <T> ArrayType<T> ofPrimitive(Class<T> type) {
39          if (boolean.class == type) {
40              return (ArrayType<T>) BooleanArrayType.INSTANCE;
41          } else if (byte.class == type) {
42              return (ArrayType<T>) ByteArrayType.INSTANCE;
43          } else if (char.class == type) {
44              return (ArrayType<T>) CharArrayType.INSTANCE;
45          } else if (double.class == type) {
46              return (ArrayType<T>) DoubleArrayType.INSTANCE;
47          } else if (float.class == type) {
48              return (ArrayType<T>) FloatArrayType.INSTANCE;
49          } else if (int.class == type) {
50              return (ArrayType<T>) IntArrayType.INSTANCE;
51          } else if (long.class == type) {
52              return (ArrayType<T>) LongArrayType.INSTANCE;
53          } else if (short.class == type) {
54              return (ArrayType<T>) ShortArrayType.INSTANCE;
55          } else {
56              throw new IllegalArgumentException(String.valueOf(type));
57          }
58      }
59  
60      default Object newInstance(int length) { return copy(empty(), length); }
61  
62      /** System.arrayCopy with same source and destination */
63      default Object copyRange(Object array, int from, int to) {
64          final int length = to - from;
65          return copy(array, length, from, 0, length);
66      }
67  
68      /** Repeatedly group an array into equal sized sub-trees */
69      default Object grouped(Object array, int groupSize) {
70          final int arrayLength = lengthOf(array);
71          final Object results = obj().newInstance(1 + ((arrayLength - 1) / groupSize));
72          obj().setAt(results, 0, copyRange(array, 0, groupSize));
73  
74          for (int start = groupSize, i = 1; start < arrayLength; i++) {
75              final int nextLength = Math.min(groupSize, arrayLength - (i * groupSize));
76              obj().setAt(results, i, copyRange(array, start, start + nextLength));
77              start += nextLength;
78          }
79  
80          return results;
81      }
82  
83      /** clone the source and set the value at the given position */
84      default Object copyUpdate(Object array, int index, T element) {
85          final Object copy = copy(array, index + 1);
86          setAt(copy, index, element);
87          return copy;
88      }
89  
90      default Object copy(Object array, int minLength) {
91          final int arrayLength = lengthOf(array);
92          final int length = Math.max(arrayLength, minLength);
93          return copy(array, length, 0, 0, arrayLength);
94      }
95  
96      /** clone the source and keep everything after the index (pre-padding the values with null) */
97      default Object copyDrop(Object array, int index) {
98          final int length = lengthOf(array);
99          return copy(array, length, index, index, length - index);
100     }
101 
102     /** clone the source and keep everything before and including the index */
103     default Object copyTake(Object array, int lastIndex) {
104         return copyRange(array, 0, lastIndex + 1);
105     }
106 
107     /** Create a single element array */
108     default Object asArray(T element) {
109         final Object result = newInstance(1);
110         setAt(result, 0, element);
111         return result;
112     }
113 
114     /** Store the content of an iterable in an array */
115     static Object[] asArray(java.util.Iterator<?> it, int length) {
116         final Object[] array = new Object[length];
117         for (int i = 0; i < length; i++) {
118             array[i] = it.next();
119         }
120         return array;
121     }
122 
123     @SuppressWarnings("unchecked")
124     static <T> T asPrimitives(Class<?> primitiveClass, Iterable<?> values) {
125         final Object[] array = Array.ofAll(values).toJavaArray();
126         final ArrayType<T> type = of((Class<T>) primitiveClass);
127         final Object results = type.newInstance(array.length);
128         for (int i = 0; i < array.length; i++) {
129             type.setAt(results, i, (T) array[i]);
130         }
131         return (T) results;
132     }
133 
134     final class BooleanArrayType implements ArrayType<Boolean>, Serializable {
135         private static final long serialVersionUID = 1L;
136         static final BooleanArrayType INSTANCE = new BooleanArrayType();
137         static final boolean[] EMPTY = new boolean[0];
138 
139         private static boolean[] cast(Object array) { return (boolean[]) array; }
140 
141         @Override
142         public Class<Boolean> type() { return boolean.class; }
143 
144         @Override
145         public boolean[] empty() { return EMPTY; }
146 
147         @Override
148         public int lengthOf(Object array) { return (array != null) ? cast(array).length : 0; }
149 
150         @Override
151         public Boolean getAt(Object array, int index) { return cast(array)[index]; }
152 
153         @Override
154         public void setAt(Object array, int index, Boolean value) throws ClassCastException {
155             if (value != null) {
156                 cast(array)[index] = value;
157             } else {
158                 throw new ClassCastException();
159             }
160         }
161 
162         @Override
163         public Object copy(Object array, int arraySize, int sourceFrom, int destinationFrom, int size) {
164             return (size > 0)
165                     ? copyNonEmpty(array, arraySize, sourceFrom, destinationFrom, size)
166                     : new boolean[arraySize];
167         }
168         private static Object copyNonEmpty(Object array, int arraySize, int sourceFrom, int destinationFrom, int size) {
169             final boolean[] result = new boolean[arraySize];
170             System.arraycopy(array, sourceFrom, result, destinationFrom, size); /* has to be near the object allocation to avoid zeroing out the array */
171             return result;
172         }
173     }
174 
175     final class ByteArrayType implements ArrayType<Byte>, Serializable {
176         private static final long serialVersionUID = 1L;
177         static final ByteArrayType INSTANCE = new ByteArrayType();
178         static final byte[] EMPTY = new byte[0];
179 
180         private static byte[] cast(Object array) { return (byte[]) array; }
181 
182         @Override
183         public Class<Byte> type() { return byte.class; }
184 
185         @Override
186         public byte[] empty() { return EMPTY; }
187 
188         @Override
189         public int lengthOf(Object array) { return (array != null) ? cast(array).length : 0; }
190 
191         @Override
192         public Byte getAt(Object array, int index) { return cast(array)[index]; }
193 
194         @Override
195         public void setAt(Object array, int index, Byte value) throws ClassCastException {
196             if (value != null) {
197                 cast(array)[index] = value;
198             } else {
199                 throw new ClassCastException();
200             }
201         }
202 
203         @Override
204         public Object copy(Object array, int arraySize, int sourceFrom, int destinationFrom, int size) {
205             return (size > 0)
206                     ? copyNonEmpty(array, arraySize, sourceFrom, destinationFrom, size)
207                     : new byte[arraySize];
208         }
209         private static Object copyNonEmpty(Object array, int arraySize, int sourceFrom, int destinationFrom, int size) {
210             final byte[] result = new byte[arraySize];
211             System.arraycopy(array, sourceFrom, result, destinationFrom, size); /* has to be near the object allocation to avoid zeroing out the array */
212             return result;
213         }
214     }
215 
216     final class CharArrayType implements ArrayType<Character>, Serializable {
217         private static final long serialVersionUID = 1L;
218         static final CharArrayType INSTANCE = new CharArrayType();
219         static final char[] EMPTY = new char[0];
220 
221         private static char[] cast(Object array) { return (char[]) array; }
222 
223         @Override
224         public Class<Character> type() { return char.class; }
225 
226         @Override
227         public char[] empty() { return EMPTY; }
228 
229         @Override
230         public int lengthOf(Object array) { return (array != null) ? cast(array).length : 0; }
231 
232         @Override
233         public Character getAt(Object array, int index) { return cast(array)[index]; }
234 
235         @Override
236         public void setAt(Object array, int index, Character value) throws ClassCastException {
237             if (value != null) {
238                 cast(array)[index] = value;
239             } else {
240                 throw new ClassCastException();
241             }
242         }
243 
244         @Override
245         public Object copy(Object array, int arraySize, int sourceFrom, int destinationFrom, int size) {
246             return (size > 0)
247                     ? copyNonEmpty(array, arraySize, sourceFrom, destinationFrom, size)
248                     : new char[arraySize];
249         }
250         private static Object copyNonEmpty(Object array, int arraySize, int sourceFrom, int destinationFrom, int size) {
251             final char[] result = new char[arraySize];
252             System.arraycopy(array, sourceFrom, result, destinationFrom, size); /* has to be near the object allocation to avoid zeroing out the array */
253             return result;
254         }
255     }
256 
257     final class DoubleArrayType implements ArrayType<Double>, Serializable {
258         private static final long serialVersionUID = 1L;
259         static final DoubleArrayType INSTANCE = new DoubleArrayType();
260         static final double[] EMPTY = new double[0];
261 
262         private static double[] cast(Object array) { return (double[]) array; }
263 
264         @Override
265         public Class<Double> type() { return double.class; }
266 
267         @Override
268         public double[] empty() { return EMPTY; }
269 
270         @Override
271         public int lengthOf(Object array) { return (array != null) ? cast(array).length : 0; }
272 
273         @Override
274         public Double getAt(Object array, int index) { return cast(array)[index]; }
275 
276         @Override
277         public void setAt(Object array, int index, Double value) throws ClassCastException {
278             if (value != null) {
279                 cast(array)[index] = value;
280             } else {
281                 throw new ClassCastException();
282             }
283         }
284 
285         @Override
286         public Object copy(Object array, int arraySize, int sourceFrom, int destinationFrom, int size) {
287             return (size > 0)
288                     ? copyNonEmpty(array, arraySize, sourceFrom, destinationFrom, size)
289                     : new double[arraySize];
290         }
291         private static Object copyNonEmpty(Object array, int arraySize, int sourceFrom, int destinationFrom, int size) {
292             final double[] result = new double[arraySize];
293             System.arraycopy(array, sourceFrom, result, destinationFrom, size); /* has to be near the object allocation to avoid zeroing out the array */
294             return result;
295         }
296     }
297 
298     final class FloatArrayType implements ArrayType<Float>, Serializable {
299         private static final long serialVersionUID = 1L;
300         static final FloatArrayType INSTANCE = new FloatArrayType();
301         static final float[] EMPTY = new float[0];
302 
303         private static float[] cast(Object array) { return (float[]) array; }
304 
305         @Override
306         public Class<Float> type() { return float.class; }
307 
308         @Override
309         public float[] empty() { return EMPTY; }
310 
311         @Override
312         public int lengthOf(Object array) { return (array != null) ? cast(array).length : 0; }
313 
314         @Override
315         public Float getAt(Object array, int index) { return cast(array)[index]; }
316 
317         @Override
318         public void setAt(Object array, int index, Float value) throws ClassCastException {
319             if (value != null) {
320                 cast(array)[index] = value;
321             } else {
322                 throw new ClassCastException();
323             }
324         }
325 
326         @Override
327         public Object copy(Object array, int arraySize, int sourceFrom, int destinationFrom, int size) {
328             return (size > 0)
329                     ? copyNonEmpty(array, arraySize, sourceFrom, destinationFrom, size)
330                     : new float[arraySize];
331         }
332         private static Object copyNonEmpty(Object array, int arraySize, int sourceFrom, int destinationFrom, int size) {
333             final float[] result = new float[arraySize];
334             System.arraycopy(array, sourceFrom, result, destinationFrom, size); /* has to be near the object allocation to avoid zeroing out the array */
335             return result;
336         }
337     }
338 
339     final class IntArrayType implements ArrayType<Integer>, Serializable {
340         private static final long serialVersionUID = 1L;
341         static final IntArrayType INSTANCE = new IntArrayType();
342         static final int[] EMPTY = new int[0];
343 
344         private static int[] cast(Object array) { return (int[]) array; }
345 
346         @Override
347         public Class<Integer> type() { return int.class; }
348 
349         @Override
350         public int[] empty() { return EMPTY; }
351 
352         @Override
353         public int lengthOf(Object array) { return (array != null) ? cast(array).length : 0; }
354 
355         @Override
356         public Integer getAt(Object array, int index) { return cast(array)[index]; }
357 
358         @Override
359         public void setAt(Object array, int index, Integer value) throws ClassCastException {
360             if (value != null) {
361                 cast(array)[index] = value;
362             } else {
363                 throw new ClassCastException();
364             }
365         }
366 
367         @Override
368         public Object copy(Object array, int arraySize, int sourceFrom, int destinationFrom, int size) {
369             return (size > 0)
370                     ? copyNonEmpty(array, arraySize, sourceFrom, destinationFrom, size)
371                     : new int[arraySize];
372         }
373         private static Object copyNonEmpty(Object array, int arraySize, int sourceFrom, int destinationFrom, int size) {
374             final int[] result = new int[arraySize];
375             System.arraycopy(array, sourceFrom, result, destinationFrom, size); /* has to be near the object allocation to avoid zeroing out the array */
376             return result;
377         }
378     }
379 
380     final class LongArrayType implements ArrayType<Long>, Serializable {
381         private static final long serialVersionUID = 1L;
382         static final LongArrayType INSTANCE = new LongArrayType();
383         static final long[] EMPTY = new long[0];
384 
385         private static long[] cast(Object array) { return (long[]) array; }
386 
387         @Override
388         public Class<Long> type() { return long.class; }
389 
390         @Override
391         public long[] empty() { return EMPTY; }
392 
393         @Override
394         public int lengthOf(Object array) { return (array != null) ? cast(array).length : 0; }
395 
396         @Override
397         public Long getAt(Object array, int index) { return cast(array)[index]; }
398 
399         @Override
400         public void setAt(Object array, int index, Long value) throws ClassCastException {
401             if (value != null) {
402                 cast(array)[index] = value;
403             } else {
404                 throw new ClassCastException();
405             }
406         }
407 
408         @Override
409         public Object copy(Object array, int arraySize, int sourceFrom, int destinationFrom, int size) {
410             return (size > 0)
411                     ? copyNonEmpty(array, arraySize, sourceFrom, destinationFrom, size)
412                     : new long[arraySize];
413         }
414         private static Object copyNonEmpty(Object array, int arraySize, int sourceFrom, int destinationFrom, int size) {
415             final long[] result = new long[arraySize];
416             System.arraycopy(array, sourceFrom, result, destinationFrom, size); /* has to be near the object allocation to avoid zeroing out the array */
417             return result;
418         }
419     }
420 
421     final class ShortArrayType implements ArrayType<Short>, Serializable {
422         private static final long serialVersionUID = 1L;
423         static final ShortArrayType INSTANCE = new ShortArrayType();
424         static final short[] EMPTY = new short[0];
425 
426         private static short[] cast(Object array) { return (short[]) array; }
427 
428         @Override
429         public Class<Short> type() { return short.class; }
430 
431         @Override
432         public short[] empty() { return EMPTY; }
433 
434         @Override
435         public int lengthOf(Object array) { return (array != null) ? cast(array).length : 0; }
436 
437         @Override
438         public Short getAt(Object array, int index) { return cast(array)[index]; }
439 
440         @Override
441         public void setAt(Object array, int index, Short value) throws ClassCastException {
442             if (value != null) {
443                 cast(array)[index] = value;
444             } else {
445                 throw new ClassCastException();
446             }
447         }
448 
449         @Override
450         public Object copy(Object array, int arraySize, int sourceFrom, int destinationFrom, int size) {
451             return (size > 0)
452                     ? copyNonEmpty(array, arraySize, sourceFrom, destinationFrom, size)
453                     : new short[arraySize];
454         }
455         private static Object copyNonEmpty(Object array, int arraySize, int sourceFrom, int destinationFrom, int size) {
456             final short[] result = new short[arraySize];
457             System.arraycopy(array, sourceFrom, result, destinationFrom, size); /* has to be near the object allocation to avoid zeroing out the array */
458             return result;
459         }
460     }
461 
462     final class ObjectArrayType implements ArrayType<Object>, Serializable {
463         private static final long serialVersionUID = 1L;
464         static final ObjectArrayType INSTANCE = new ObjectArrayType();
465         static final Object[] EMPTY = new Object[0];
466 
467         private static Object[] cast(Object array) { return (Object[]) array; }
468 
469         @Override
470         public Class<Object> type() { return Object.class; }
471 
472         @Override
473         public Object[] empty() { return EMPTY; }
474 
475         @Override
476         public int lengthOf(Object array) { return (array != null) ? cast(array).length : 0; }
477 
478         @Override
479         public Object getAt(Object array, int index) { return cast(array)[index]; }
480 
481         @Override
482         public void setAt(Object array, int index, Object value) {
483             cast(array)[index] = value;
484         }
485 
486         @Override
487         public Object copy(Object array, int arraySize, int sourceFrom, int destinationFrom, int size) {
488             return (size > 0)
489                     ? copyNonEmpty(array, arraySize, sourceFrom, destinationFrom, size)
490                     : new Object[arraySize];
491         }
492         private static Object copyNonEmpty(Object array, int arraySize, int sourceFrom, int destinationFrom, int size) {
493             final Object[] result = new Object[arraySize];
494             System.arraycopy(array, sourceFrom, result, destinationFrom, size); /* has to be near the object allocation to avoid zeroing out the array */
495             return result;
496         }
497     }
498 }